package com.litchi.tutor.common.annotation;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;

/**
 * 参数解析器
 *
 * @author xyouzi
 * @since 2021-11-26
 */
@Slf4j
public class RequestBodyParamMethodArgumentResolver implements HandlerMethodArgumentResolver {

    private static final String POST = "post";
    private static final String PUT = "put";
    private static final String APPLICATION_JSON = "application/json";

    /**
     * 判断是否需要处理此参数
     * @param parameter 方法的参数
     * @return 如果此参数带有 @RequestSingleBody 注解则返回 {@code true}, 否则返回 {@code false}
     */
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(RequestBodyParam.class);
    }

    /**
     * 解析参数
     * @param parameter 方法的参数
     * @param mavContainer 上下文容器, 主要是承担着整个请求过程中数据的传递工作
     * @param webRequest WebRequest 接口的扩展, 用于获取本机请求和响应对象
     * @param binderFactory 字符串类型转换为字节类型的转换工具
     * @return 请求参数
     */
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
        String contentType = Objects.requireNonNull(servletRequest).getContentType();
        String requestMethod = servletRequest.getMethod();
        if (contentType == null || !contentType.contains(APPLICATION_JSON)) {
            log.error("[RequestSingleBody] contentType must be " + APPLICATION_JSON);
            throw new RuntimeException("[RequestSingleBody] contentType must be " + APPLICATION_JSON);
        }

        if (!POST.equalsIgnoreCase(requestMethod) && !PUT.equalsIgnoreCase(requestMethod)) {
            log.error("[RequestSingleBody] requestMethod must be " + POST + "or " + PUT);
            throw new RuntimeException("[RequestSingleBody] requestMethod must be " + POST + "or " + PUT);
        }

        return this.bindRequestParams(parameter, servletRequest);
    }

    /**
     * 绑定请求参数
     * @param parameter 方法的参数
     * @param servletRequest 客户端的请求
     * @return 请求参数
     */
    private Object bindRequestParams(MethodParameter parameter, HttpServletRequest servletRequest) {
        RequestBodyParam requestSingleBody = parameter.getParameterAnnotation(RequestBodyParam.class);
        Class<?> parameterType = parameter.getParameterType();
        String requestBody = this.getRequestBody(servletRequest);
        log.info(requestBody);
        Map paramObj = JSONObject.parseObject(requestBody, Map.class);
        if (paramObj == null) {
            paramObj = new JSONObject();
        }

        String parameterName = StringUtils.isBlank(requestSingleBody.value()) ? parameter.getParameterName() : requestSingleBody.value();
        Object value = paramObj.get(parameterName);

        // 方便測試
//        log.info("parameterName:{}, value:{}", parameterName, value);

        if (requestSingleBody.required()) {
            if (value == null) {
                log.error("[RequestSingleBody] " + parameterName + " is required");
                throw new RuntimeException("[RequestSingleBody] " + parameterName + " is required");
            }
        }
//        System.out.println("bindRequestParams convert value: " + value);
//        System.out.println("bindRequestParams convert parameterType: " + parameterType);
//        System.out.println("bindRequestParams convert: " + ConvertUtils.convert(value, parameterType));
        return ConvertUtils.convert(value, parameterType);
    }

    /**
     * 获取请求体
     * @param servletRequest 请求
     * @return 请求体字符串
     */
    private String getRequestBody(HttpServletRequest servletRequest) {
        StringBuilder stringBuilder = new StringBuilder();
        try {
            BufferedReader reader = servletRequest.getReader();
            char[] buf = new char[1024];
            int length;
            while ((length = reader.read(buf)) != -1) {
                stringBuilder.append(buf, 0, length);
            }
            log.info("看這裡：{}",stringBuilder);
        } catch (IOException e) {
            log.error("[RequestSingleBody] read request body error", e);
            throw new RuntimeException("[RequestSingleBody] read request body error");
        }
//        System.out.println("getRequestBody stringBuilder: " + stringBuilder.toString());
        return stringBuilder.toString();
    }
}
